/*
    Copyright 2008, 2009, 2010 Brightworks, Inc.

    This file is part of Language Mentor.

    Language Mentor is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Language Mentor is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Language Mentor.  If not, see <http://www.gnu.org/licenses/>.
*/
package com.langcollab.languagementor.model
{
    import com.brightworks.util.Debug;
    import com.brightworks.util.Utils_Array;
    import com.brightworks.util.Utils_DataConversion;
    import com.brightworks.util.Utils_XML;
    import com.brightworks.util.tree.Utils_Tree;
    import com.brightworks.vo.VO;
    import com.langcollab.languagementor.constant.Constant_Misc;
    import com.langcollab.languagementor.vo.*;

    import flash.filesystem.File;

    import mx.collections.ArrayCollection;
    import mx.collections.Sort;
    import mx.collections.SortField;

    [Bindable]
    public class DataManager
    {
        public var model:MainModel;
        public var modulesSelectionTreeSortOptions:Array;
        public var modulesSelectionTreeSortSelections:Array;

        private var _callbacks:Callbacks;
        private var _currentNativeLanguageResourceXML:XML;
        private var _currentNativeLanguageVO:LanguageVO;
        private var _currentTargetLanguageResourceXML:XML;
        private var _currentTargetLanguageVO:LanguageVO;
        private var _data:Data;


        // ****************************************************
        //
        //          Public Methods
        //
        // ****************************************************

        public function DataManager(model:MainModel) 
        {
            this.model = model;
        }

        /*public function deleteData(deleteCriteriaVO:VO, callbacks:Callbacks):void
        {
            _data.deleteData(deleteCriteriaVO);//, callbacks);
        }*/

        public function getChunkFileVOFromChunkVOAndLanguageId(vo:ChunkVO, languageId:int):ChunkFileVO {
            if (!_data.index_chunkFileVOs_by_chunkVO_plus_languageId[vo]) 
                Debug.error_Fatal(["DataManager.getChunkFileVOFromChunkVOAndLanguageId(): index_chunkFileVOs_by_chunkVO_plus_languageId has no property matching vo", _data.index_chunkFileVOs_by_chunkVO_plus_languageId, vo]);
            if (!_data.index_chunkFileVOs_by_chunkVO_plus_languageId[vo][languageId]) 
                Debug.error_Fatal(["DataManager.getChunkFileVOFromChunkVOAndLanguageId(): index_chunkFileVOs_by_chunkVO_plus_languageId[vo] doesn't contain a propery matching languageId", _data.index_chunkFileVOs_by_chunkVO_plus_languageId, vo, languageId]);
            var allMatchingVOs:Array = _data.index_chunkFileVOs_by_chunkVO_plus_languageId[vo][languageId];
            if (allMatchingVOs.length != 1) 
                Debug.error_Fatal(["DataManager.getChunkFileVOFromChunkVOAndLanguageId(): index_chunkFileVOs_by_chunkVO_plus_languageId[vo][languageId] should be a one element array - instead it contains " + allMatchingVOs.length + " elements", _data.index_chunkFileVOs_by_chunkVO_plus_languageId, vo, languageId]);
            var result:ChunkFileVO = allMatchingVOs[0];
            return result;
        }

        public function getChunkVOsSortedByLocationInOrderFromModuleVersionVO(vo:ModuleVersionVO):ArrayCollection
        {
            var result:ArrayCollection = new ArrayCollection();
            var sort:Sort = new Sort();
            sort.fields = [new SortField("locationInOrder")];
            result.sort = sort;
            result.refresh();
            var chunkVOList:Array = _data.index_chunkVOs_by_moduleVersionVO[vo];
            var chunkVO:ChunkVO;
            for each (chunkVO in chunkVOList)
            {
                result.addItem(chunkVO);
            }
            return result;
        }

        public function getContentProviderDisplayNameFromModuleVersionVO(vo:ModuleVersionVO):String {
            var contentProviderId:String = vo.contentProviderId;
            var displayName:String = _data.index_contentProviderDisplayName_by_contentProviderId[contentProviderId];
            if (displayName == null) 
                Debug.error_Fatal(["DataManager.getContentProviderDisplayNameFromModuleVersionVO(): Can't retrieve a contentProviderDisplayName for contentProviderId of passed in VO", vo]);
            return displayName;
        }

        public function getLanguageIdFromIso639_3Code(code:String):int {
            if (code == null) {
                Debug.error_Fatal("DataManager.getLanguageIdFromIso639_3Code(): passed code is null");
            }
            return _data.index_language_id_by_iso639_3Code[code];
        }

        public function getLanguageNameTranslation(languageResourceXML:XML, inWhatLanguageLanguageCode:String):String {
            var languageNameTranslationsNode:XML = languageResourceXML.languageNameTranslations[0];
            if (!languageNameTranslationsNode.hasOwnProperty(inWhatLanguageLanguageCode)) return null;
            var result:String = languageNameTranslationsNode[inWhatLanguageLanguageCode][0];
            return result;
        }

        public function getLanguageNameTranslation_ForNativeLanguage_InNativeLanguage():String {
            // Translation: "Get the native language's name"
            // Yes, this is kind of a silly method name, but I'm using it for consistency with
            // getLanguageNameTranslation_ForTargetLanguage_InNativeLanguage(). They both use
            // the same mechanism: getLanguageNameTranslation()
            return getLanguageNameTranslation(_currentNativeLanguageResourceXML, _currentNativeLanguageVO.iso639_3Code);
        }

        public function getLanguageNameTranslation_ForTargetLanguage_InNativeLanguage():String {
            return getLanguageNameTranslation(_currentTargetLanguageResourceXML, _currentNativeLanguageVO.iso639_3Code);
        }

        public function getLanguageVOFromID(id:int):LanguageVO {
            return _data.index_languageVO_by_id[id];
        }

        public function getLearningFeedbackIdFromLabelToken(labelToken:String):int {
            return _data.index_learningFeedback_id_by_labelToken[labelToken];
        }

        public function getLearningModeIDListSortedByLocationInOrder():Array {
            var index:Object = _data.index_learningMode_id_by_locationInOrder;
            var result:Array = Utils_DataConversion.convertObjectToArrayConsistingOfValuesSortedByPropertyNames(index, false, false, true);
            return result;
        }

        public function getLearningModeTokenFromID(id:int):String {
            return getLearningModeVOFromID(id).labelToken;
        }

        public function getLearningModeVOFromID(id:int):LearningModeVO {
            return _data.index_learningModeVO_by_id[id];
        }

        public function getLevelIdFromNativeLanguageLevelLabel(label:String):int {
            var levelIdString:String;
            var levelId:int;
            var candidateLabel:String;
            for (levelIdString in _data.index_levelVO_by_id) {
                levelId = int(levelIdString);
                candidateLabel = getNativeLanguageLevelLabelFromLevelId(levelId);
                if (candidateLabel == label) return levelId;
            }
            Debug.error_Fatal("DataManager.getNativeLanguageLevelIdFromLevelLabel(): Can't find match for label: + label");
            return 0;
        }

        public function getLevelLabelTokenFromId(id:int):String
        {
            return getLevelVOFromID(id)["labelToken"];
        }

        public function getLevelLocationInOrderFromLevelId(id:int):int {
            var levelVO:LevelVO = getLevelVOFromID(id);
            var locationInOrder:int = levelVO.locationInOrder;
            return locationInOrder;
        }

        public function getLevelVOFromID(id:int):LevelVO
        {
            return _data.index_levelVO_by_id[id];
        }

        public function getLevelOrderLevelFromId(id:int):String
        {
            return getLevelVOFromID(id)["locationInOrder"]
        }

        public function getModuleVersionCount_ForCurrentNativeAndTargetLanguages():int {
            var result:int = 0;
            var moduleVersionList:Array = _data.selectData(new ModuleVersionVO());
            var mvVO:ModuleVersionVO;
            var mvnlVOList:Array;
            var mvtlVOList:Array;
            for each (mvVO in moduleVersionList) {
                mvnlVOList = _data.index_moduleVersionNativeLanguageVOs_by_moduleVersionVO[mvVO];
                mvtlVOList = _data.index_moduleVersionTargetLanguageVOs_by_moduleVersionVO[mvVO];
                if (!Utils_Array.doesArrayContainObjectWithSpecifiedValueForSpecifiedProp(mvnlVOList, _currentNativeLanguageVO.id, "languageId")) {
                    continue;
                }
                if (!Utils_Array.doesArrayContainObjectWithSpecifiedValueForSpecifiedProp(mvtlVOList, _currentTargetLanguageVO.id, "languageId")) {
                    continue;
                }
                result ++;
            }
            return result;
        }

        public function getModuleVersionChunkCountFromModuleVersionVO(vo:ModuleVersionVO):int
        {
            return _data.index_chunkVOs_by_moduleVersionVO[vo].length;
        }

        public function getModuleVersionVOsSortedByNativeLanguageNameFromModuleVersionVOList(moduleVersionVOList:ArrayCollection):ArrayCollection
        {
            var result:ArrayCollection = new ArrayCollection();
            var tempList:ArrayCollection = new ArrayCollection();
            var sort:Sort = new Sort();
            var sortField:SortField = new SortField("displayName", true);
            sort.fields = [sortField];
            tempList.sort = sort;
            tempList.refresh();
            var moduleVersionVO:ModuleVersionVO;
            for each (moduleVersionVO in moduleVersionVOList) {
                var nativeLanguageName:String = getModuleVersionNativeLanguageNameFromModuleVersionVO(moduleVersionVO);
                var data:Object = {vo:moduleVersionVO, displayName:nativeLanguageName};
                tempList.addItem(data);
            }
            for each (data in tempList) {
                result.addItem(data.vo);
            }
            return result;
        }

        public function getModuleVersionNativeLanguageCreditsXMLFromModuleVersionVO(vo:ModuleVersionVO):String {
            var mvnlVO:ModuleVersionNativeLanguageVO = getModuleVersionNativeLanguageVOFromModuleVersionVOAndLanguageID(vo, _currentNativeLanguageVO.id);
            var result:String = mvnlVO.creditsXML;
            if (result == null) result = "";
            return result;
        }

        public function getModuleVersionNativeLanguageDescriptionFromModuleVersionVO(vo:ModuleVersionVO):String {
            var mvnlVO:ModuleVersionNativeLanguageVO = getModuleVersionNativeLanguageVOFromModuleVersionVOAndLanguageID(vo, _currentNativeLanguageVO.id);
            var result:String = mvnlVO.description;
            if (result == null) result = "";
            return result;
        }

        public function getModuleVersionNativeLanguageNameFromModuleVersionVO(vo:ModuleVersionVO):String {
            var mvnlVO:ModuleVersionNativeLanguageVO = getModuleVersionNativeLanguageVOFromModuleVersionVOAndLanguageID(vo, _currentNativeLanguageVO.id);
            return mvnlVO.moduleName;
        }

        public function getModuleVersionNativeLanguageVOFromModuleVersionVOAndLanguageID(vo:ModuleVersionVO, languageId:int):ModuleVersionNativeLanguageVO {
            if (!_data.index_moduleVersionNativeLanguageVOs_by_moduleVersionVO_plus_languageId[vo]) 
                Debug.error_Fatal(["DataManager.getModuleVersionNativeLanguageVOFromModuleVersionVOAndLanguageID(): index_moduleVersionNativeLanguageVOs_by_moduleVersionVO_plus_languageId has no property matching vo", _data.index_moduleVersionNativeLanguageVOs_by_moduleVersionVO_plus_languageId, vo]);
            if (!_data.index_moduleVersionNativeLanguageVOs_by_moduleVersionVO_plus_languageId[vo][languageId]) 
                Debug.error_Fatal(["DataManager.getModuleVersionNativeLanguageVOFromModuleVersionVOAndLanguageID(): index_moduleVersionNativeLanguageVOs_by_moduleVersionVO_plus_languageId[vo] doesn't contain a propery matching languageId", _data.index_moduleVersionNativeLanguageVOs_by_moduleVersionVO_plus_languageId, vo, languageId]);
            var allMatchingVOs:Array = _data.index_moduleVersionNativeLanguageVOs_by_moduleVersionVO_plus_languageId[vo][languageId];
            if (allMatchingVOs.length != 1) 
                Debug.error_Fatal(["DataManager.getModuleVersionNativeLanguageVOFromModuleVersionVOAndLanguageID(): index_moduleVersionNativeLanguageVOs_by_moduleVersionVO_plus_languageId[vo][languageId] should be a one element array - instead it contains " + allMatchingVOs.length + " elements", _data.index_moduleVersionNativeLanguageVOs_by_moduleVersionVO_plus_languageId, vo, languageId]);
            var result:ModuleVersionNativeLanguageVO = allMatchingVOs[0];
            return result;
        }

        public function getModuleVersionTargetLanguageVOFromModuleVersionVOAndLanguageID(vo:ModuleVersionVO, languageId:int):ModuleVersionTargetLanguageVO {
            if (!_data.index_moduleVersionTargetLanguageVOs_by_moduleVersionVO_plus_languageId[vo]) 
                Debug.error_Fatal(["DataManager.getModuleVersionTargetLanguageVOFromModuleVersionVOAndLanguageID(): index_moduleVersionTargetLanguageVOs_by_moduleVersionVO_plus_languageId has no property matching vo", _data.index_moduleVersionTargetLanguageVOs_by_moduleVersionVO_plus_languageId, vo]);
            if (!_data.index_moduleVersionTargetLanguageVOs_by_moduleVersionVO_plus_languageId[vo][languageId]) 
                Debug.error_Fatal(["DataManager.getModuleVersionTargetLanguageVOFromModuleVersionVOAndLanguageID(): index_moduleVersionTargetLanguageVOs_by_moduleVersionVO_plus_languageId[vo] doesn't contain a propery matching languageId", _data.index_moduleVersionTargetLanguageVOs_by_moduleVersionVO_plus_languageId, vo, languageId]);
            var allMatchingVOs:Array = _data.index_moduleVersionTargetLanguageVOs_by_moduleVersionVO_plus_languageId[vo][languageId];
            if (allMatchingVOs.length != 1) 
                Debug.error_Fatal(["DataManager.getModuleVersionTargetLanguageVOFromModuleVersionVOAndLanguageID(): index_moduleVersionTargetLanguageVOs_by_moduleVersionVO_plus_languageId[vo][languageId] should be a one element array - instead it contains " + allMatchingVOs.length + " elements", _data.index_moduleVersionTargetLanguageVOs_by_moduleVersionVO_plus_languageId, vo, languageId]);
            var result:ModuleVersionTargetLanguageVO = allMatchingVOs[0];
            return result;
        }

        public function getModuleVersionVOFromChunkVO(vo:ChunkVO):ModuleVersionVO {
            var result:ModuleVersionVO = _data.index_chunkVOs_by_moduleVersionVO[vo];
            return result;
        }

        public function getNativeChunkFileVOFromChunkVO(vo:ChunkVO):ChunkFileVO {
            var result:ChunkFileVO = getChunkFileVOFromChunkVOAndLanguageId(vo, _currentNativeLanguageVO.id);
            return result;
        }

        public function getNativeLanguageLevelLabelFromLevelId(id:int):String {
            var labelToken:String = getLevelLabelTokenFromId(id);
            var label:String      = getNativeLanguageResource("label_Level_" + labelToken);
            return label;
        }

        public function getNativeLanguageResource(type:String):String {
            var result:String;
            switch (type) {
                case "iso639_3Code":
                    result = _currentNativeLanguageVO[type];
                    break;
                default:
                    result = _currentNativeLanguageResourceXML[type].toString();
                    if (result == "") 
                        Debug.error_Fatal(["DataManager.getNativeLanguageResource(): 'type' arg is '" + type + "' - no resource by this name in native language resource XML (i.e. for '" + _currentNativeLanguageVO.iso639_3Code + "' language)", "_currentNativeLanguageResourceXML:", _currentNativeLanguageResourceXML]);
            }
            return result;
        }

        public function getTargetLanguageChunkFileDurationFromChunkVO(vo:ChunkVO):int {
            var cfVO:ChunkFileVO = getTargetChunkFileVOFromChunkVO(vo);
            var result:int = cfVO.duration;
            return result;
        }

        public function getTargetChunkFileVOFromChunkVO(vo:ChunkVO):ChunkFileVO {
            var result:ChunkFileVO = getChunkFileVOFromChunkVOAndLanguageId(vo, _currentTargetLanguageVO.id);
            return result;
        }

        public function getTargetLanguageResource(type:String):String {
            var result:String;
            switch (type) {
                case "iso639_3Code":
                    result = _currentTargetLanguageVO[type];
                    break;
                default:
                    result = _currentTargetLanguageResourceXML[type].toString();
                    if (result == "") 
                        Debug.error_Fatal(["DataManager.getTargetLanguageResource(): 'type' arg is '" + type + "' - no resource by this name in target language resource XML (i.e. for '" + _currentTargetLanguageVO.iso639_3Code + "' language)", "_currentTargetLanguageResourceXML:", _currentTargetLanguageResourceXML]);
            }
            return result;
        }

        public function init(callbacks:Callbacks):void
        {
            _callbacks = callbacks;
            /// Kludge init code - should we even be using the DB & VOs for this? The XML files are much easier to update
            _currentNativeLanguageVO = new LanguageVO();
            _currentNativeLanguageVO.id            = 1;
            _currentNativeLanguageVO.iso639_3Code  = "eng";
            _currentTargetLanguageVO = new LanguageVO();
            _currentTargetLanguageVO.id            = 2;
            _currentTargetLanguageVO.iso639_3Code  = "cmn";
            /// When this is implemented, we should check to make sure that:
            //     a. We have XML language resource files for both langs
            //     b. They contain name translations for one another - we can use getLanguageNameTranslation() for this
            // If not, we should try to download new files and try again before giving the user a "big problem" error message
            // Of course, these files should be checked for a lot of things at some earlier point, probably on the server


            loadLanguageResourceXML();
            var dataCallbacks:Callbacks = new Callbacks(onDataInitResult, onDataInitError);
            _data = new Data();
            _data.init(dataCallbacks);
        }

        public function initModuleData():void
        {
            var moduleVersionList:Array = _data.selectData(new ModuleVersionVO());
            var tempModuleData:ArrayCollection = new ArrayCollection();
            var moduleVersionDescriptorObject:Object;
            for each (var vo:ModuleVersionVO in moduleVersionList)
            {
                moduleVersionDescriptorObject = new Object();
                moduleVersionDescriptorObject.contentProviderDisplayString   = getContentProviderDisplayNameFromModuleVersionVO(vo);
                moduleVersionDescriptorObject.levelDisplayString             = getNativeLanguageLevelLabelFromLevelId(vo.levelId);
                moduleVersionDescriptorObject.levelLocationInOrder           = getLevelLocationInOrderFromLevelId(vo.levelId);
                moduleVersionDescriptorObject.moduleVersionVO                = vo;
                moduleVersionDescriptorObject.nameDisplayString              = getModuleVersionNativeLanguageNameFromModuleVersionVO(vo);
                tempModuleData.addItem(moduleVersionDescriptorObject);
            }
            var dataSource:ArrayCollection = Utils_Tree.createDataSourceObject(tempModuleData, modulesSelectionTreeSortOptions);
            model.moduleSelectionTreeData = dataSource;
        }

        public function insertData(queryDataList:Array, callbacks:Callbacks):void
        {
            _data.insertData(queryDataList, callbacks)
        }

        public function selectData(selectionCriteriaVO:VO):Array {
            return _data.selectData(selectionCriteriaVO);
        }

        /*public function updateData(queryDataList:Array, callbacks:Callbacks):void
        {
            _data.updateData(queryDataList, callbacks);
        }*/

        public function uploadableDataExists():Boolean
        {
            // This would look through the data for data types that have an
            // 'uploaded' field - if any are true, then return true
            return false;
        }

        // ****************************************************
        //
        //          Private Methods
        //
        // ****************************************************

        private function initModulesSelectionTreeSortOptions():void
        {
            modulesSelectionTreeSortOptions = new Array();
            /* modulesSelectionTreeSortOptions.push(  {
                                                        name:"Level",
                                                        levelGroupingProp:"level",
                                                        customSort:true,
                                                        customSortFieldKeyProp:"levelId",
                                                        customSortFieldIndex:_data.index_level_locationInOrder_by_id
                                                } ); */
            modulesSelectionTreeSortOptions.push(  {
                                                       name:"Content Provider",
                                                       displayAndGroupingProp:"contentProviderDisplayString",
                                                       customSort:false
                                                   } );
            modulesSelectionTreeSortOptions.push(  {
                                                       name:"Level",
                                                       displayAndGroupingProp:"levelDisplayString",
                                                       customSort:true,
                                                       locationInOrderProp:"levelLocationInOrder"
                                                   } );
            modulesSelectionTreeSortOptions.push(  {
                                                       name:"Name",
                                                       displayAndGroupingProp:"nameDisplayString",
                                                       customSort:false
                                                   } );
        }

        private function loadLanguageResourceXML():void 
        {
            var path:String;
            var appDir:File = File.applicationDirectory;
            var f:File;
            var s:String = Constant_Misc.FILEPATHINFO_LANGUAGE_RESOURCE_FOLDER_NAME + File.separator + _currentNativeLanguageVO.iso639_3Code + ".xml";
            f = appDir.resolvePath(Constant_Misc.FILEPATHINFO_LANGUAGE_RESOURCE_FOLDER_NAME + File.separator + _currentNativeLanguageVO.iso639_3Code + ".xml");
            _currentNativeLanguageResourceXML = Utils_XML.synchronousLoadXML(f);
            f = appDir.resolvePath(Constant_Misc.FILEPATHINFO_LANGUAGE_RESOURCE_FOLDER_NAME + File.separator + _currentTargetLanguageVO.iso639_3Code + ".xml");
            _currentTargetLanguageResourceXML = Utils_XML.synchronousLoadXML(f);
        }

        private function onDataInitError(info:Object):void 
        {
            _callbacks.fault(info);
            throw new Error("DataManager.onDataInitError() called");
        }

        private function onDataInitResult(info:Object):void 
        {
            initModulesSelectionTreeSortOptions();
            initModuleData();
            model.dataManagerInitialized = true;
            _callbacks.result(info);
        }

    }
}





